home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / infor / ddj_more.zip / MORE.C next >
Text File  |  1993-01-04  |  17KB  |  631 lines

  1. #include <bios.h>
  2. #include <stdio.h>
  3. #include <ctype.h>
  4. #include <fcntl.h>
  5. #include <process.h>
  6.  
  7. /*    MORE.C        Page input to stdout.
  8.  *
  9.  *    (C) 1986, Allen I. Holub.  All rights reserved.
  10.  *
  11.  *    Usage:  more  [-<offset>]  file...
  12.  *
  13.  *    Exit status: 0 always
  14.  */
  15.  
  16. /*------------------------------------------------------------------------*/
  17.  
  18. #define b_getc()    bioskey(0)
  19. #define look()        bioskey(1)
  20.  
  21. /*------------------------------------------------------------------------*/
  22.  
  23. #define CAN    0x18    /* ^X    */
  24. #define ESC    0x1b    /* ^[    */
  25.  
  26. #define max(a,b)  ((a) > (b) ? (a) : (b))
  27. #define min(a,b)  ((a) < (b) ? (a) : (b))
  28.  
  29. #define BSIZE      256    /* Maximum length of a line in the file */
  30. #define PAGESIZE  23    /* # of lines to output before stopping */
  31.  
  32. #define E(x)        fprintf(stderr,"%s\n",x)
  33. #define HAS_DOT(p)    strchr(p,'.')
  34.  
  35. FILE    *Ifile        = stdin;    /* Current input file            */
  36. char    *Ifile_name    = "/dev/con";    /* Name of file associated w/ Ifile */
  37. int    Repeat_count    = 1;        /* Repeat count for most recent cmd */
  38. long    Line        = 0;        /* # of output lines printed so far */
  39. long    Flen        = 0;        /* Length of input file in chars    */
  40. long    Start_here    = 0;        /* Seek to here when prog starts    */
  41.  
  42. /*--------------------------------------------------------------------------
  43.  *  Stack used to keep track of start of line.  Maximum number
  44.  *  of lines is determined by STACKSIZE.
  45.  */
  46.  
  47. typedef    long        STACKTYPE;
  48. #define    STACKSIZE    (1024*6)    /* Must be divisible by 2 */
  49.  
  50. STACKTYPE        Stack[ STACKSIZE ];
  51. STACKTYPE        *Sp = Stack + STACKSIZE;
  52.  
  53. #define    STACKFULL    (Sp <= Stack)
  54. #define    STACKEMPTY    (Sp >= Stack + STACKSIZE)
  55. #define    CLEAR_STACK()    Sp = Stack + STACKSIZE ;
  56. #define TOS        (STACKEMPTY ? 0 : *Sp)
  57. #define BACK_SCRN    *( min( Sp+(PAGESIZE-1), Stack+(STACKSIZE-1)) )
  58.  
  59. #define    erase_line()    line( ' ', 0 )    /* Draw a line of spaces            */
  60.  
  61. /*--------------------------------------------------------------------------*/
  62.  
  63. help()
  64. {
  65.     register int    i;
  66.  
  67.     /*  Print a help message with a box around it, special IBM
  68.      *  graphics characters are used for the box.
  69.      */
  70.  
  71.     putc( 0xd6, stderr );
  72.     for( i=56 ; --i >= 0 ; putc( 0xc4, stderr) )
  73.         ;
  74.     E("\267");
  75.     E("\272 b ............... go (B)ack a page                     \272");
  76.     E("\272 e ............... go to end of file                    \272");
  77.     E("\272 n ............... go to (N)ext file                    \272");
  78.     E("\272 o ............... print (O)ffset from start of file    \272");
  79.     E("\272 q ............... (Q)uit (return to DOS)               \272");
  80.     E("\272 s ............... (S)kip one line (w/o printing)       \272");
  81.     E("\272 r ............... (R)ewind file (go back to begining)  \272");
  82.     E("\272 ! ............... execute a program (type blank line   \272");
  83.     E("\272                   at prompt to execute last)           \272");
  84.     E("\272 / ............... search for regular expression        \272");
  85.     E("\272                   (type blank line at prompt for last) \272");
  86.     E("\272 ESC ............. Scroll until any key is hit          \272");
  87.     E("\272 CR .............. next line                            \272");
  88.     E("\272 SP .............. next screen                          \272");
  89.     E("\272 anything else ... print this list                      \272");
  90.     E("\272                                                        \272");
  91.     E("\272 All commands may be preceeded by a count.              \272");
  92.  
  93.     putc( 0xd3, stderr );
  94.     for( i=56 ; --i >= 0 ; putc( 0xc4, stderr ) )
  95.     ;
  96.     E("\275");
  97. }
  98. /*--------------------------------------------------------------------------*/
  99.  
  100. usage()
  101. {
  102.     E("more:  Copyright (C) 1986, Allen I. Holub. All rights reserved.");
  103.     E("\nUsage: more [+<num>] [file...] \n");
  104.     E("Print all files in list on the screen, pausing every 23 lines");
  105.     E("If + is specified, more will start printing at character <num>");
  106.     E("One of the following commands is executed after each page:");
  107.  
  108.     help();
  109.     exit(1);
  110. }
  111.  
  112. /*--------------------------------------------------------------------------*/
  113.  
  114. push( long file_posn )            /* Push file_posn onto the stack    */
  115. {
  116.     if( STACKFULL )            /* If stack is full, compress it    */
  117.     comp_stk();
  118.  
  119.     *( --Sp ) = file_posn;
  120. }
  121. /*--------------------------------------------------------------------------*/
  122.  
  123. long pop()
  124. {
  125.     /*  Pop one entry off the stack and return the file
  126.      *  position
  127.      */
  128.  
  129.     return STACKEMPTY ? 0 : *Sp++ ;
  130. }
  131. /*--------------------------------------------------------------------------*/
  132.  
  133. comp_stk()
  134. {
  135.     /*  Compress the stack by removing every other entry.
  136.      *  This routine is called when the stack is full (we've
  137.      *  read more lines than the stack can hold).
  138.      */
  139.  
  140.     register STACKTYPE        *dest, *src;
  141.  
  142.     fprintf(stderr,"\007Stack Full:  Compressing\n");
  143.  
  144.     src = dest = Stack + STACKSIZE;
  145.  
  146.     while( (src -= 2) >= Stack )
  147.     *--dest = *src;
  148.  
  149.     Sp = dest;
  150. }
  151. /*--------------------------------------------------------------------------*/
  152.  
  153. getcon()
  154. {
  155.     /*  Get one character from the console using a direct
  156.      *  BIOS call.  Map \r into \n if one is encountered.
  157.      */
  158.  
  159.     register int c;
  160.  
  161.     c = b_getc() & 0x7f;    /* Get a character from console */
  162.     putchar(c);            /* Echo character               */
  163.  
  164.     return (c == '\r' ) ? '\n' : c ;
  165. }
  166. /*--------------------------------------------------------------------------*/
  167.  
  168. clear_io()
  169. {
  170.     /*  Clears the entire I/O queue, both at the BIOS and the
  171.      *  BDOS level.
  172.      */
  173.  
  174.     while( look() )
  175.     b_getc();
  176.  
  177. #ifdef NEVER
  178.     while( kbhit() )
  179.     getchar();
  180. #endif
  181.  
  182. }
  183. /*--------------------------------------------------------------------------*/
  184.  
  185. khit()                /* Return true if a key has been hit on the */
  186. {                /* physical keyboard (as compared with a    */
  187.     if( look() )        /* character available on stdin).           */
  188.     {
  189.     clear_io();
  190.     return 1;
  191.     }
  192.     return 0;
  193. }
  194. /*--------------------------------------------------------------------------*/
  195.  
  196. char *getbuf( register char *p )
  197. {
  198.     /*  Get a line of input using direct console I/O and put it
  199.      *  into buf.  Return a pointer to the first whitespace on the
  200.      *  line or to the end of line if none.  This routine is for
  201.      *  getting commands from the user, not for getting normal
  202.      *  input.  ^H is supported as a destructive backspace but no
  203.      *  other editing is available.
  204.      */
  205.  
  206.     register int    c;
  207.     int            gottail = 0;
  208.     char        *start  = p;
  209.     char        *tail   = "";
  210.  
  211.     clear_io();
  212.  
  213.     while( (c=getcon()) != '\n' )
  214.     {
  215.     if( c == '\b' )
  216.     {
  217.         if( p <= start )
  218.         fputs( "!\007", stderr );
  219.         else
  220.         {
  221.         --p;
  222.         fputs( " \b", stderr );
  223.         }
  224.     }
  225.     else
  226.     {
  227.         if( isspace(c) && !gottail )
  228.         gottail = (int)( tail = p );
  229.         *p++ = c;
  230.     }
  231.     }
  232.  
  233.     *p = '\0';
  234.     return( p <= start ? NULL : tail );
  235. }
  236. /*--------------------------------------------------------------------------*/
  237.  
  238. percent( s )
  239. char    *s;
  240. {
  241.     /*  Print the percentage of the file that we've seen so far  */
  242.  
  243.     printf("%4.1f%%%s", ((double)TOS / (double)Flen) * 100.00, s );
  244. }
  245. /*--------------------------------------------------------------------------*/
  246.  
  247. int getcmd()
  248. {
  249.     /*  Get a command from the keyboard, using direct
  250.      *  bios I/O.  Commands take the form [num]<c>.  Returns
  251.      *  the command.  Repeat_count is initialized to hold [num]
  252.      *  or 1 if no num is entered.
  253.      */
  254.  
  255.     int c;
  256.  
  257.     clear_io();
  258.     percent("");
  259.     printf(", line %ld (? for commands): ", Line );
  260.  
  261.     Repeat_count = 0;
  262.     while( '0' <= (c=getcon())  &&  c <= '9' )
  263.     Repeat_count = (Repeat_count * 10) + (c - '0');
  264.  
  265.     if( Repeat_count == 0 )
  266.     Repeat_count = 1;
  267.  
  268.     erase_line();
  269.  
  270.     if( c == 0x03 )                /* ^C == abort */
  271.     exit(1);
  272.  
  273.     return( c );
  274. }
  275. /*--------------------------------------------------------------------------*/
  276.  
  277. char *inputline( suppress )
  278. {
  279.     /*  Get a line from the file being processed and put it into
  280.      *  buf.  Push the start of line character onto the stack.
  281.      *  return 0 on end of file, a pointer to the line (i.e. to buf)
  282.      *  otherwise.
  283.      */
  284.  
  285.     register int    rval;
  286.     register long    start_of_line;
  287.     static   char    buf[BSIZE];
  288.  
  289.     start_of_line = ftell( Ifile );
  290.  
  291.     if( rval = (int) fgets(buf, BSIZE, Ifile) )
  292.     {
  293.     Line++;
  294.     push( start_of_line );
  295.     if( !suppress )
  296.         fputs( buf, stdout );
  297.     }
  298.  
  299.     return rval ? buf : NULL ;
  300. }
  301. /*--------------------------------------------------------------------------*/
  302.  
  303. printpage()
  304. {
  305.     /*  Print an entire page from the input file  */
  306.  
  307.     register int i;
  308.  
  309.     for( i=PAGESIZE-1 ; --i>=0 && inputline(0) ; )
  310.     ;
  311. }
  312. /*--------------------------------------------------------------------------*/
  313.  
  314. search()
  315. {
  316.     /*  Prompt for a pattern and then search for it in the
  317.      *  file.  Stop searching if the pattern is found or if
  318.      *  any key is hit.  The previous pattern is remembered
  319.      *  in a local array so, if CR is entered instead of a
  320.      *  pattern, the previous pattern is used.
  321.      */
  322.  
  323.     static    char    pat[128], opat[128];
  324.     char        *iline;
  325.     extern    int    *makepat();
  326.     extern    char    *matchs();
  327.     int            *template;
  328.  
  329.     printf("/");
  330.  
  331.     if( !getbuf( pat ) )
  332.     strcpy( pat, opat );
  333.  
  334.     if( !(template = makepat( pat, 0 )) )
  335.     printf("Illegal regular expression: %s\n", pat );
  336.     else
  337.     {
  338.     erase_line();
  339.     printf( "/%s\n", pat );
  340.  
  341.         while( (iline = inputline(1)) && !khit() )
  342.     {
  343.         percent("\r");
  344.         if( matchs( iline, template, 0 ) )
  345.         break;
  346.     }
  347.  
  348.         unmakepat( template );
  349.     fseek( Ifile, pop(), 0 );    /* Back up one line */
  350.     --Line;
  351.     line( 0xcd, 1 );
  352.     printpage();
  353.     }
  354.  
  355.     strcpy( opat, pat );
  356. }
  357. /*--------------------------------------------------------------------------*/
  358.  
  359. execute()
  360. {
  361.     /*  Spawn off a child process.  When the process terminates
  362.      *  print a message and redraw the current page.  Note that
  363.      *  spawn() is used (rather than system()) so you can't
  364.      *  execute a batch file or a built in command.  This
  365.      *  subruotine will set the CMDLINE environment variable
  366.      *  to a null string for the sake of those routines that
  367.      *  are executing under the shell which will use it.
  368.      */
  369.  
  370.     static    char    buf[128];
  371.     char        *tail = " ";
  372.  
  373.     static    char    obuf[128], *otail = obuf;
  374.  
  375.     register    char    *p;
  376.     register    int    c;
  377.  
  378.     printf("!");
  379.  
  380.     if( !(tail = getbuf(buf)) )        /* If no command entered, */
  381.     {                    /* use the same one we    */
  382.     tail = otail;            /* used last time.        */
  383.     memcpy( buf, obuf, 128 );
  384.     printf( "\n!%s\n", buf );
  385.     }
  386. #ifdef NEVER
  387.     else
  388.     {
  389.     if( *tail )
  390.         *tail++ = '\0';
  391.     }
  392.  
  393.     if( HAS_DOT(buf) )
  394.     {
  395.     /*  Spawnlp will actually try to execute any file that you
  396.      *  give it.  If you say to execute an ASCII file, it will
  397.      *  load that file into memory, try to execute it, and die
  398.      *  a horrible death.  We attempt to avoid this by checking
  399.      *  for a dot in the file name.  You may want to put a more
  400.      *  rigorous test here.
  401.      */
  402.  
  403.         fprintf(stderr, "\007<%s> is not a command\n", buf);
  404.     }
  405.     else
  406.     {
  407.     putenv("CMDLINE=");
  408.     if( spawnlp(P_WAIT, buf, buf, tail, NULL) == -1 )
  409.         fprintf(stderr, "Can't execute <%s %s>\n", buf, tail );
  410.     }
  411. #endif
  412.  
  413.     putenv("CMDLINE=");
  414.     if( system( buf ) )
  415.     fprintf(stderr, "Can't execute <%s>\n", buf );
  416.  
  417.     printf("Hit any key to continue ....");
  418.     getcon();
  419.     erase_line();
  420.     putchar('\n');
  421.  
  422.     otail = tail;
  423.     memcpy( obuf, buf, 128 );
  424. }
  425. /*--------------------------------------------------------------------------*/
  426.  
  427. line( c, newline )
  428. {
  429.     /*  Print a line of characters to mark top of page.  0xcd
  430.      *  is the IBM graphics character for a horizontal double
  431.      *  line.  The cursor is put at the begining of next line
  432.      *  if "newline" is true, else it's put at begining of
  433.      *  current line.
  434.      */
  435.  
  436.     register    int    i;
  437.  
  438.     putchar('\r');
  439.  
  440.     for( i = 79 ; --i >= 0 ; putchar( c ) )
  441.     ;
  442.  
  443.     putchar( newline ? '\n' : '\r' );
  444. }
  445. /*--------------------------------------------------------------------------*/
  446.  
  447. backapage( count )
  448. {
  449.     /*  Go back count pages and print the resulting page.  */
  450.  
  451.     register    int    i;
  452.  
  453.     i = ((count+1) * PAGESIZE) - 3;
  454.  
  455.     while( --i >= 0 )
  456.     {
  457.     Line--;
  458.     pop();
  459.     }
  460.  
  461.     line( 0xcd, 1 );
  462.     fseek( Ifile, pop(), 0 );
  463.     Line = max( Line - 1, 0 );
  464.     printpage();
  465. }
  466. /*--------------------------------------------------------------------------*/
  467.  
  468. docmd( cmd, ateof )
  469. {
  470.     /*  Do a single command, return 1 if next file is requested.
  471.      *  Actually call exit on a "quit" command or ^C.
  472.      */
  473.  
  474.     register    int    rval = 0;
  475.     register    int    i;
  476.     long        posn;
  477.  
  478.     do{
  479.     switch( cmd )
  480.     {
  481.     case CAN:   break;            /* NOP */
  482.     case 'q':   exit(0);            /* abort */
  483.     case '\n':  if( ateof )          /* Forward motion */
  484.                 rval = 1;        /* one line */
  485.             else
  486.                 inputline(0);
  487.             break;
  488.     case ' ':   if( ateof )            /* one page */
  489.                 rval = 1;
  490.             printpage();
  491.             break;
  492.     case 'e':   erase_line();        /* To end of file */
  493.             while( inputline(1)  &&  !khit() )
  494.                 percent("\r");
  495.             break;
  496.     case 's':   if( ateof )            /* one line w/o printing */
  497.                 rval=1;
  498.             else
  499.             {
  500.                 erase_line();
  501.                 inputline(1);
  502.                 percent("\r");
  503.             }
  504.             break;
  505.         case ESC:   if( ateof )            /* scroll till key is hit */
  506.             rval = 1;
  507.             else
  508.                 while( inputline(0) && !khit() )
  509.                 clear_io();
  510.             clear_io();
  511.             Repeat_count = 0;        /* Ignore repeat count */
  512.             break;            /* if it's set.        */
  513.         case 'n':   rval = 1;            /* to next file        */
  514.             break;
  515.     case '/':   search();            /* search for pattern */
  516.             break;
  517.     case 'r':   line( 0xcd, 1 );        /* to start of file */
  518.             CLEAR_STACK();
  519.             Line = 0;
  520.             fseek( Ifile, 0L, 0 );
  521.             printpage();
  522.             break;
  523.     case 'b':   backapage( Repeat_count );    /* to a previous page */
  524.             Repeat_count = 0;
  525.             break;
  526.     case 'o':   printf("Top line = %ld, ", BACK_SCRN );  /* print file */
  527.             printf("Bottom line = %ld\n", TOS);      /* position   */
  528.             break;
  529.     case '!':   /*  Close the file and spawn another shell.
  530.              *  When we come back, reopen the file and
  531.              *  position to the same place we were before.
  532.              *  This is necessary because of a bug in
  533.              *  Microsoft C ver. 3.0's spawn functions
  534.              *  (they trash the IOB).  It will cause
  535.              *  problems if standard input is used as
  536.              *  the input source (as in a pipe) because
  537.              *  we won't be able to successfully reopen
  538.              *  stdin.
  539.              */
  540.  
  541.                     Repeat_count = 0;        /* Ignore repeat count */
  542. /*            fclose( Ifile );*/
  543.             execute();
  544.             posn = pop();
  545.  
  546. /*                    if( Ifile = fopen(Ifile_name, "r") )
  547.             {
  548.             fseek( Ifile, posn, 0 );*/
  549.             backapage( 0 );
  550. /*            }
  551.             else
  552.             {
  553.             fprintf(stderr,"more: can't open %s\n",
  554.                             Ifile_name);
  555.             rval = 1;
  556.             }*/
  557.             break;
  558.     default:    help();            /* Print the help message   */
  559.             cmd = getcmd();        /* get a new command        */
  560.             Repeat_count++;
  561.             break;
  562.     }
  563.     } while( --Repeat_count > 0 );
  564.     return( rval );
  565. }
  566. /*--------------------------------------------------------------------------*/
  567.  
  568. dofile( char *fname )
  569. {
  570.     /*  Process lines from an input file having the indicated
  571.      *  name.
  572.      */
  573.  
  574. #ifdef DEBUG
  575.     fprintf( stderr, "File name: %s\n", fname );
  576. #endif
  577.  
  578.     if( (Ifile_name = fname) && !(Ifile = fopen(fname, "r")) )
  579.     fprintf(stderr, "more: can't open %s\n", fname );
  580.     else
  581.     {
  582.     Flen = filelength( fileno(Ifile) );
  583.     fseek( Ifile, Start_here, 0 );
  584.  
  585.         CLEAR_STACK();
  586.     docmd( ' ', 0 );        /* dump the first page */
  587.  
  588.         for( ; ; )
  589.     {
  590.         for( ; ; )
  591.         {
  592.         if( docmd( getcmd(), 0 ) )
  593.             return;
  594.         if( feof( Ifile ) )
  595.             break;
  596.         }
  597.         E("\n\020\020\020 LAST LINE IN FILE \021\021\021");
  598.         if( docmd( getcmd(), 1 ) )
  599.         break;
  600.     }
  601.     fclose( Ifile );
  602.     }
  603. }
  604. /*--------------------------------------------------------------------------*/
  605.  
  606. main( int argc, char **argv )
  607. {
  608. #ifdef DEBUG
  609.     fprintf( stderr, "%s %s", *argv, argv[1] );
  610. #endif
  611.  
  612.     if( argc > 1 )
  613.     {
  614.     if( argv[1][0] == '-' )
  615.         usage();
  616.     else if( argv[1][0] == '+' )
  617.     {
  618.         Start_here = atol( &argv[1][1] );
  619.         printf("Starting at character %ld\n", Start_here );
  620.         push( Start_here );
  621.         ++argv;
  622.         --argc;
  623.     }
  624.     }
  625.     if( argc <= 1 )
  626.     dofile( NULL );
  627.     else
  628.         for( ; --argc > 0 ; dofile(*(++argv)) )
  629.         ;
  630.     exit(0);
  631. }